1 module cgi.cgi;
2 /*****************************************************************************
3  * package: cgi
4  * module cgi.cgi
5  * File: cgi.d
6  * Description:  utility d module that contains Classes to build cgi applications.
7  * Author: Joseph M. Rice (ricejm01@gmail.com)
8  * Date: Wed Dec 18 00:04:03 EST 2014
9  *
10  *MIT License
11  *
12  *Copyright (c) 2014-2016 Joseph M. Rice <ricejm01@gmail.com>
13  *
14  *Permission is hereby granted, free of charge, to any person obtaining a copy
15  *of this software and associated documentation files (the "Software"), to deal
16  *in the Software without restriction, including without limitation the rights
17  *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18  *copies of the Software, and to permit persons to whom the Software is
19  *furnished to do so, subject to the following conditions:
20  *
21  *The above copyright notice and this permission notice shall be included in all
22  *copies or substantial portions of the Software.
23  *
24  *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25  *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27  *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29  *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  *SOFTWARE.
31  ***************************************************************************/
32 
33 import std.stdio;
34 import std..string;
35 import std.conv;
36 import std.process;
37 import std.algorithm;
38 import std.uri;
39 import std.exception;
40 
41 
42 class CGIException : Exception
43 {
44     this(string msg, string file = __FILE__, size_t line = __LINE__) {
45         super(msg, file, line);
46     }
47 }
48 
49 /**
50 * this enumeration allows us to describe the type 
51 * of content the cgi class will be returning and 
52 * allows us to programatically control the behavior
53 * of how the what the application is writing to stdout.
54 * I.E. if content is generally text/html generate the html headers
55 *	   or if we are a text/plain (possibly a .csv or .txt) file 
56 *      behave this way.  
57 */
58 enum MIMETYPE {
59 	TEXT_HTML=0,			
60 	TEXT_HTML_NO_CACHE,
61 	TEXT_HTML_RAW,
62 	TEXT_HTML_NO_CACHE_RAW,
63 	TEXT_PLAIN,
64 	TEXT_PLAIN_NO_CACHE,
65 	RAW_TYPE
66 }
67 
68 /**
69 * Abritrary modes that cgi variable can have
70 * 
71 * Normal mode is that the variable is not sticky
72 * Forward mode is that upon the next form submission or
73 * 	post/get the cgi varibles should be sent along to be
74 *	"sticky" to keep state.  
75 */
76 enum CGIMODES {
77 	NORMAL=0,
78 	FORWARD=1
79 }
80 
81 /**
82  * Abritrary modes or states that a cgi variable could be
83  * 
84  * Normal mode is the varibale is data
85  * File mode is to indicate the varible is a file.
86  */
87 enum CGI_VAR_TYPE {
88 	NORMAL=0,
89 	FILE=1
90 }
91 
92 /**
93 * CGIFILE class is a data structure that describes the way a 
94 * file is reprensented from the web server.  (apache)
95 * 
96 * Note: currently a glorifed Struct, a class with no methods
97 * 		as that future plans are to expand this class.
98 */
99 class CGIFILE {
100 	string filename;
101 	string content_type;
102 	string content;
103 
104 	this() {		
105 	}
106 }
107 
108 /**
109 * CGIVALS class is a data structure that represents the value
110 * key pairs that a cgi query string represents.  IE.
111 * ("?foo=bar&foobar=be") where this query string has two CGIVALs
112 * contained within. this class allows us to store variables and 
113 * logically describe what those variables are and assign a mode. 
114 */
115 class CGIVALS {
116 	string name;
117 	string value;
118 	CGIMODES mode;
119 	CGI_VAR_TYPE type;
120 	CGIFILE fData;	
121 
122 	this() {
123 		name = "";
124 		value = "";
125 		mode = CGIMODES.NORMAL;
126 		type = CGI_VAR_TYPE.NORMAL;
127 		fData = new CGIFILE();
128 	}
129 }
130 
131 /**
132  * see it's a COOKIE class and it's good enough for me.  
133  *
134  * COOKIE class is a data structure that represents the components 
135  * of an html cookie.  
136  */
137 class COOKIE {
138 	string data;
139 	string expires;
140 	string path;
141 	string domain;
142 
143 	this() {
144 		this.data = "";
145 		this.expires = "";
146 		this.path = "";
147 		this.domain = "";
148 	}
149 
150 	this(string http_cookie) {
151 		parseCookieString(http_cookie);
152 	}
153 
154 	/**
155 	 * parse the cookie string and break into key=value pairs
156 	 *
157 	 *Note: this function is simplistic and assumes ("data payload"; expires= ; path= ; domain= ;) for the cookie string.
158      * so when you use cookies you have to have them in this format.   To do: is make this method smarter. 	 
159 	 */
160 	void parseCookieString(string http_cookie) {
161 		if (http_cookie.length > 0) {
162 			auto token = findSplit(http_cookie,"; expires=");
163 
164 			if (token[1] == "; expires=") {
165 				this.data = chompPrefix(chomp(strip(token[0].idup),"\""),"\"");
166 				token = findSplit(token[2],"; path=");
167 			}
168 
169 			if (token[1] == "; path=") {
170 				this.expires = strip(token[0].idup);
171 				token = findSplit(token[2],"; domain=");
172 			}
173 
174 			if (token[1] == "; domain=") {
175 				this.path = strip(token[0].idup);
176 				this.domain = strip(token[2].idup);
177 			}
178 		} else {
179 			this.data = "";
180 			this.expires = "";
181 			this.path = "";
182 			this.domain = "";
183 		}
184 	}
185 
186 	/**
187 	 * return a string reprenstation in html of the cookie
188 	 */
189 	override string toString() {
190 		return data~"; expires="~expires~"; path="~path~";";// domain="~domain~";";
191 	}
192 }
193 
194 
195 /**
196 * CGI class 
197 *
198 * Designed to read the environment variables created by web server and 
199 * will consume the data and make it easily accessible to program with. 
200 */
201 class CGI {
202 	//
203 	// Class Varibles
204 	//
205 	CGIVALS[] cgiVals;
206 	string HTTP_USER_AGENT;
207 	string REQUEST_METHOD;
208 	string PATH_INFO;
209 	string CONTENT_TYPE;
210 	string CONTENT_LENGTH;
211 	string QUERY_STRING;
212 	string SCRIPT_NAME;
213 	string SERVER_NAME;
214 	string BASE_URL;
215 	string HTTP_COOKIE;
216 	string SERVER_PORT;
217 	string HTTP_HOST;
218 	string REQUEST_URI;
219 	string HTTPS;
220 	string REMOTE_ADDR;
221 	private string custom_content_type;
222 	char[] postData;
223 	private MIMETYPE mime_type;
224 	private int mime_type_set;
225 	private int CGI_ARRAY_START_SIZE=25;
226 	private string error_msg;
227 	private COOKIE cookie;
228 
229 	//
230 	// Constructor(s)
231 	//
232 	this() {
233 		mime_type_set = 0;
234 		this.init();
235 	}
236 
237 	this(MIMETYPE type) {
238 		mime_type = type;
239 		mime_type_set=1;
240 		this.init();
241 	}
242 
243 	~this() {
244 		this.destroyCgiData();
245 	}
246 	//
247 	// Private Methods
248 	//
249 	private void init() {
250 		int ret = -9;
251 		//
252 		//Read environment varibles. 
253 		//
254 		REQUEST_METHOD = environment.get("REQUEST_METHOD");
255 		HTTP_USER_AGENT = environment.get("HTTP_USER_AGENT");
256 		PATH_INFO = environment.get("PATH_INFO");
257 		CONTENT_TYPE = environment.get("CONTENT_TYPE");
258 		CONTENT_LENGTH = environment.get("CONTENT_LENGTH");
259 		QUERY_STRING = environment.get("QUERY_STRING");
260 		SCRIPT_NAME = environment.get("SCRIPT_NAME");
261 		SERVER_NAME = environment.get("SERVER_NAME");
262 		SERVER_PORT = environment.get("SERVER_PORT");
263 		HTTP_COOKIE = environment.get("HTTP_COOKIE");
264 		HTTP_HOST = environment.get("HTTP_HOST");
265 		REQUEST_URI = environment.get("REQUEST_URI");
266 		HTTPS = environment.get("HTTPS");
267 		REMOTE_ADDR = environment.get("HTTP_X_REAL_IP"); // Nginx proxy forwarding
268 		if (REMOTE_ADDR.length <= 0) {
269 			REMOTE_ADDR = environment.get("REMOTE_ADDR");
270 		}
271 
272 		cookie = new COOKIE();
273 
274 		if (HTTPS != "on") {
275 			BASE_URL = "http://"~ SERVER_NAME ~ SCRIPT_NAME;
276 		} else {
277 			BASE_URL = "https://"~ SERVER_NAME ~ SCRIPT_NAME;
278 		}
279 
280 		if (REQUEST_METHOD == "POST" || 
281 			REQUEST_METHOD == "PUT") {
282 			if (CONTENT_LENGTH.length == 0) {
283 				throw new CGIException("ERROR: CONTENT_LENGTH environment variable is not present!");
284 			}
285 
286 			int buff_size = to!int(CONTENT_LENGTH);
287 
288 			if (buff_size != 0) {
289 				// set our buffer size.  
290 				postData = new char[buff_size];
291 				// Read it all at once :)
292 				stdin.rawRead(postData);
293 			} else {
294 				// there is nothing to read so just set an empty buffer
295 				postData = new char[0];
296 
297 			}
298 
299 			if (CONTENT_TYPE == "application/x-www-form-urlencoded")
300 				QUERY_STRING ~= postData.idup;
301 		} 
302 
303 		// the foling are just handleded because it assumed that
304 		// we already grabbed the needed environment variables
305 		// and we will be leaving it up to the application developer
306 		// to check method type if they care about it.  
307 		//REQUEST_METHOD == "DELETE"	
308 		//REQUEST_METHOD == "GET"
309 
310 		this.initCGIData();		
311 	}
312 
313 	private int initMultipartCGIData(string postData) {
314 		string boundary;
315 		string haystack;
316 		string data;
317 		string content_disp;
318 		string content_type;
319 		string content;
320 		string filename;
321 		string name;
322 		bool isFile = false;
323 		int i = 0;
324 		int ret = 1;
325 		
326 		//
327 		// set cgivals array start size
328 		//
329 		if (cgiVals.length == 0) {
330 			cgiVals.length = CGI_ARRAY_START_SIZE;  
331 		}
332 
333 		//
334 		//read bountry string from CONTENT_TYPE;
335 		//
336 		//if (CONTENT_TYPE != "multipart/form-data") {
337 		if (!(startsWith(CONTENT_TYPE,"multipart/form-data;"))) {
338 			ret = -2; 
339 			return ret;
340 		}
341 
342 		auto boundary_split = findSplit(CONTENT_TYPE, "boundary=");
343 
344 		if (boundary_split[2].length <= 0) {
345 			throw new CGIException("ERROR: Could not locate multipart/form-data boundry value!");
346 		}
347 
348 		// for some reason the CONTENT_TYPE boundary does not have the correct 
349 		// number of '-' So I'm adding 2 so everything lines up. 
350 		boundary = "--" ~ boundary_split[2].idup;
351 		//
352 		// skip the start boundary so we can logically findSplit
353 		//
354 		auto token = findSplit(postData,boundary); // skip the first boundry.
355 		//
356 		// Parse multipart/form-data
357 		//
358 		haystack = token[2].idup;
359 
360 		//we know to end when our haystack is "--\r\n"
361 		for (i = 0; haystack.length > 0 && haystack != "--\r\n"; i++) {
362 			token = findSplit(haystack,boundary);
363 			//
364 			// Grow our cgi array if we need too. 
365 			//
366 			if (i == cgiVals.length)
367 				cgiVals.length *= 2;
368 			//
369 			// The content-disposition
370 			//
371 			data = token[0].idup;
372 	
373 			// ok, the boundary also has a "\r\n" at the end, except for the end it has "--\r\n"
374 			// so our data now begins with "\r\n" so we have to account for this.  
375  			auto data_token = findSplit(data, "\r\n");
376 			data_token = findSplit(data_token[2], "\r\n");
377 
378 			content_disp = data_token[0].idup;
379 			//
380 			// The content-type
381 			//
382 			data_token = findSplit(data_token[2], "\r\n");
383 			content_type = data_token[0].idup;
384 
385 			//
386 			// The content
387 			//
388 			content = chomp(chompPrefix(data_token[2].idup,"\r\n"),"\r\n");
389 			//
390 			//parse content_disposition
391 			//
392 			//Content-Disposition: form-data; name="file"; filename="package.json"
393 			//
394 			// Find filename
395 			//
396 			data_token = findSplit(content_disp,"filename=");
397 			if (data_token[2].length) {
398 				// we have a file name and we are a file
399 				isFile = true;
400 				filename = chomp(chompPrefix(data_token[2],"\""),"\"");
401 			}
402 			//
403 			// Find name
404 			//
405 			data_token = findSplit(content_disp,"name=");
406 			if (isFile) {
407 				data_token = findSplit(data_token[2],";");
408 				name = chomp(chompPrefix(data_token[0],"\""),"\"");
409 			} else {
410 				name = chomp(chompPrefix(data_token[2],"\""),"\"");
411 			}
412 
413 			//
414 			// Store the parsed data
415 			//
416 			cgiVals[i] = new CGIVALS;
417 
418 			cgiVals[i].name = name.idup; //name 
419 			if(!isFile) {
420 				cgiVals[i].value = content.idup; //value
421 			} else {
422 				cgiVals[i].value = "";
423 				cgiVals[i].type = CGI_VAR_TYPE.FILE;
424 				cgiVals[i].fData.filename = filename.idup;
425 				cgiVals[i].fData.content_type = content_type.idup;
426 				cgiVals[i].fData.content = content.idup;
427 			}
428 			//
429 			// Make the Haystack smaller
430 			//
431 			haystack = token[2].idup;
432 		}
433 
434 		cgiVals.length = i;
435 		ret = i;
436 
437 		return ret;
438 	}
439 
440 	//look at QUERY_STRING and parse our cgivals. 
441 	private void initCGIData() {
442 		string haystack = QUERY_STRING.idup;
443 		int i = 0, ret = 0;
444 
445 		if (postData.length > 0 &&  //  CONTENT_TYPE = multipart/form-data; boundary=------------------------cdcd8bbbb646b6ae
446 			(startsWith(CONTENT_TYPE,"multipart/form-data;"))) {
447 		    //CONTENT_TYPE == "multipart/form-data") {
448 			ret = initMultipartCGIData(to!string(postData));
449 		}
450 
451 		if (ret >= 0) {
452 			if (cgiVals.length == 0) {
453 				cgiVals.length = CGI_ARRAY_START_SIZE;  
454 			} else {
455 				cgiVals.length *= 2;	
456 			}	
457 
458 			for (i = ret; haystack.length > 0 ; i++) {
459 				auto pair = findSplit(haystack,"&");
460 				auto vals = findSplit(pair[0],"=");	
461 				if (i == cgiVals.length)
462 					cgiVals.length *= 2;
463 				cgiVals[i] = new CGIVALS;
464 				cgiVals[i].name = vals[0].idup; //name 
465 				cgiVals[i].value= urlDecode(vals[2]).idup; //value
466 				haystack = pair[2].idup;
467 			}
468 			cgiVals.length = i;
469 		}
470 	}
471 
472 
473 	private void destroyCgiData() {
474 		int i = 0; 
475 		for (i = 0; i != cgiVals.length; ++i) {
476 			destroy(cgiVals[i]);
477 		}
478 	}
479 
480 	private string urlDecode(string url) {
481 		return decodeComponent(url);
482 	}
483 
484 	private string urlEncode(string url) {
485 		return encodeComponent(url);
486 	}
487 
488 	//
489 	// Class Methods
490 	//
491 
492 	/**
493 	* Check if a cgi variable exists with the passed in name.
494 	*
495 	* returns true if the variable exists.
496 	*/
497 	bool exists(string name) {
498 		bool ret = false;
499 		int i = 0;
500 
501 		for (i = 0; i != cgiVals.length; ++i) {
502 			if (cgiVals[i] && cgiVals[i].name == name) {
503 				ret = true;
504 				break;
505 			}
506 		}
507 		return ret;
508 	}
509 
510 	/**
511 	* Check if a cgi variable exists with the passed in name and index.
512 	* keep in mind that the cgi environment can have multiple variables 
513 	* with the same name the passed in index allows us to specify which 
514 	* one to check for.
515 	*
516 	* returns true if the variable exists.
517 	*/
518 	bool exists(string name, int index) {
519 		bool ret = false;
520 		int i = 0, count = 0;
521 
522 		for (i = 0; i != cgiVals.length; ++i) {
523 			if (cgiVals[i] && cgiVals[i].name == name) {
524 				if (count == index) {
525 					ret = true;
526 					break;
527 				}
528 				count++;
529 			}
530 		}
531 		return ret;
532 	}
533 
534 	/**
535 	* Get the value for the cgi variable for the passed in name
536 	*
537 	* return a string with the cgi variables value.
538 	*/
539 	string getVal(string name) {
540 		string ret = "";
541 		int i = 0;
542 
543 		for (i = 0; i != cgiVals.length; ++i) {
544 			if (cgiVals[i] && cgiVals[i].name == name) {
545 				ret = (cgiVals[i].value).idup;
546 				break;
547 			}
548 		}
549 
550 		return ret;
551 	}
552 
553 	/**
554 	* Get the value for the cgi variable for the passed in name and index.
555 	* keep in mind that the cgi environment can have multiple variables
556 	* of the same name the index allows us to specify which one we want.
557 	*
558 	* return a string with the cgi variables value.
559 	*/
560 	string getVal(string name, int index) {
561 		string ret = "";
562 		int i = 0;
563 		int count = 0;
564 
565 		for (i = 0; i != cgiVals.length; ++i) {
566 			if (cgiVals[i] && cgiVals[i].name == name) {
567 				if (count == index) {
568 					ret = (cgiVals[i].value).idup;
569 					break;
570 				} 
571 				count++;
572 			}
573 		}
574 		return ret;
575 	}
576 	
577 	/**
578 	* Since the cgi environment can have multiple variables we might 
579 	* want to get all the values.  
580 	*
581 	* return a string array of all the values for a varible name
582 	*/
583 	string[] getVal_m(string name) {
584 		string[] ret;
585 		int i = 0;
586 		int count = 0;
587 
588 		if (ret.length == 0) {
589 			ret.length = CGI_ARRAY_START_SIZE;
590 		} 
591 
592 		for (i = 0; i != cgiVals.length; ++i) {
593 
594 			if (count == ret.length) {
595 				ret.length *= 2;
596 			}
597 
598 			if (cgiVals[i] && cgiVals[i].name == name) {
599 				ret[count] = (cgiVals[i].value).idup;				
600 				count++;
601 			}
602 		}
603 		ret.length = count;
604 		return ret;
605 	}
606 
607 	/**
608   	* Instead of reading the cgi data only from the environment, 
609 	* we can set our own cgi variables from memory.
610 	*/	
611 	void setVal(string name, string value, CGIMODES mode, 
612 				CGI_VAR_TYPE type, CGIFILE fData) {
613 		ulong i = cgiVals.length;
614 
615 		cgiVals.length += 1;
616 
617 		cgiVals[i] = new CGIVALS;
618 		cgiVals[i].name = name;
619 		cgiVals[i].value = value;
620 		cgiVals[i].mode = mode;
621 		cgiVals[i].type = type;
622 		if (fData) {
623 			cgiVals[i].fData.filename = fData.filename;
624 			cgiVals[i].fData.content_type = fData.content_type;
625 			cgiVals[i].fData.content = fData.content;
626 		}
627 
628 	}
629 	/**
630 	* set the cgi Variable mode of a given name
631 	*/
632 	void setVarMode(string name, CGIMODES mode) {
633 		int i = 0;
634 
635 		for (i = 0; i != cgiVals.length; ++i) {
636 			if (cgiVals[i] && cgiVals[i].name == name) {
637 				cgiVals[i].mode = mode;
638 				break;
639 			}
640 		}
641 	}
642 
643 	/**
644 	* set the cgi Variable mode of a given name and index
645 	* Keep in mind the cgi environment can contain multiple 
646 	* variables with the same name. The index allows us to 
647 	* get the one we want.
648 	*/
649 	void setVarMode(string name, CGIMODES mode, int index) {
650 		int i = 0;
651 		int count = 0;
652 
653 		for (i = 0; i != cgiVals.length; ++i) {
654 			if (cgiVals[i] && cgiVals[i].name == name) {
655 				if (count == index) {
656 					cgiVals[i].mode = mode;
657 					break;
658 				}
659 			   count++;	
660 			}
661 		}
662 	}
663 
664 	/**
665 	* Get the cgi Variable mode of a given name.  
666 	*
667 	* returns the CGIMODES enum value for the variable with the name passed.  
668 	*/
669 	CGIMODES getVarMode(string name) {
670 		int i = 0;
671 		CGIMODES ret;
672 
673 		for (i = 0; i != cgiVals.length; ++i) {
674 			if (cgiVals[i] && cgiVals[i].name == name) {
675 				ret = cgiVals[i].mode;
676 			}
677 		}
678 		return ret;
679 	}
680 
681 	/**
682 	* Get the cgi Variable mode of a given name and index
683 	* Keep in mind the cgi environment can contain multiple 
684 	* variables with the same name. The index allows us to 
685 	* get the one we want.
686 	*
687 	* returns the CGIMODES enum value for the variable with the name passed.  
688 	*/
689 	CGIMODES getVarMode(string name, int index) {
690 		int i = 0;
691 		int count = 0;
692 		CGIMODES ret;
693 
694 		for (i = 0; i != cgiVals.length; ++i) {
695 			if (cgiVals[i] && cgiVals[i].name == name) {
696 				if (count == index) {
697 					ret = cgiVals[i].mode;
698 					break;
699 				} 
700 				count++;
701 			}
702 		}
703 		return ret;
704 	}
705 
706 	/**
707 	* create a string of the cgi variables that are in forward mode.
708     * This allows us to use cgi variables to keep state and forward 
709 	* them on to another form submission.  This allows the programmer 
710     * to selectivly forward and unforward cgi variables for flow/state 
711 	* control.
712 	*
713 	* returns a string containing query string of the variables in forward mode.	
714 	*/
715 	string getForwardVarString() {
716 		string ret = "";
717 		int i = 0;
718 
719 		for (i = 0; i != cgiVals.length; ++i) {
720 			if (cgiVals[i].mode == CGIMODES.FORWARD) {
721 				ret ~=  cgiVals[i].name ~ "=" ~ urlEncode(cgiVals[i].value);
722 				if ((i + 1) != cgiVals.length) {
723 					ret ~= "&";
724 				}
725 			}
726 		}
727 
728 		return ret;
729 	}
730 
731 	/**
732 	* Check to see if a cgi variable with name is a file or not.
733 	*
734 	* returns true if the cgi variable is file.
735 	*/
736 	bool varIsFile(string name) {
737 		bool ret = false;
738 		int i = 0;
739 
740 		for (i = 0; i != cgiVals.length; ++i) {
741 			if (cgiVals[i] &&
742 				cgiVals[i].name == name &&
743 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
744 				ret = true;
745 				break;
746 			}
747 		}
748 
749 		return ret;
750 	}
751 
752 	/**
753 	* Check to see if a cgi variable with name and index is a file or not.
754 	* keep in mind that the cgi environments may contain several variables 
755 	* with same name.  the index allows us to specify which one.
756 	*
757 	* returns true if the cgi variable is file.
758 	*/
759 	bool varIsFile(string name,int index) {
760 		bool ret = false;
761 		int i = 0;
762 		int count = 0;
763 
764 		for (i = 0; i != cgiVals.length; ++i) {
765 			if (cgiVals[i] &&
766 				cgiVals[i].name == name &&
767 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
768 				if (count == index) {
769 					ret = true;
770 					break;
771 				}
772 				count++;
773 			}
774 		}
775 
776 		return ret;
777 	}
778 
779 	/**
780 	* Get the file content type of the the file posted/put in the cgi envirnment 
781 	* with the variable string name.  
782 	*
783 	* returns a string containing the original content type of the file uploaded. 
784 	*/
785 	string getFileContentType(string name) {
786 		string ret = "";
787 		int i = 0;
788 
789 		for (i = 0; i != cgiVals.length; ++i) {
790 			if (cgiVals[i] && 
791 				cgiVals[i].name == name && 
792 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
793 				ret = cgiVals[i].fData.content_type.idup;
794 				break;
795 			}
796 		}
797 
798 		return ret;
799 	}
800 
801 	/**
802 	* Get the file content type of the the file posted/put in the cgi envirnment 
803 	* with the variable string name with the index.  
804 	* Keep in mind that a variable can exist multiple times in the cgi environment 
805 	* and the index allows us to specify exactly which one.
806 	*
807 	* returns a string containing the original content type of the file uploaded. 
808 	*/
809 	string getFileContentType(string name, int index) {
810 		string ret = "";
811 		int i = 0;
812 		int count = 0;
813 
814 		for (i = 0; i != cgiVals.length; ++i) {
815 			if (cgiVals[i] && 
816 				cgiVals[i].name == name && 
817 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
818 				if (count == index) {
819 					ret = cgiVals[i].fData.content_type.idup;
820 					break;
821 				}
822 				count++;
823 			}
824 		}
825 
826 		return ret;
827 	}
828 
829 	/**
830 	* Get the file name of the the file posted/put in the cgi envirnment 
831 	* with the variable string name. 
832 	*
833 	* returns a string containing the original name of the file uploaded. 
834 	*/
835 	string getFileName(string name) {
836 		string ret = "";
837 		int i = 0;
838 
839 		for (i = 0; i != cgiVals.length; ++i) {
840 			if (cgiVals[i] && 
841 				cgiVals[i].name == name && 
842 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
843 				ret = cgiVals[i].fData.filename.idup;
844 				break;
845 			}
846 		}
847 
848 		return ret;
849 	}
850 
851 	/**
852 	* Get the file name of the the file posted/put in the cgi envirnment 
853 	* with the variable string name with the index.  
854 	* Keep in mind that a variable can exist multiple times in the cgi environment 
855 	* and the index allows us to specify exactly which one.
856 	*
857 	* returns a string containing the original name of the file uploaded. 
858 	*/
859 	string getFileName(string name, int index) {
860 		string ret = "";
861 		int i = 0;
862 		int count = 0;
863 
864 		for (i = 0; i != cgiVals.length; ++i) {
865 			if (cgiVals[i] && 
866 				cgiVals[i].name == name && 
867 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
868 				if (count == index) {
869 					ret = cgiVals[i].fData.filename.idup;
870 					break;
871 				}
872 				count++;
873 			}
874 		}
875 
876 		return ret;
877 	}
878 
879 	/**
880 	* Get the content of a file in the cgi environment with string name.
881 	*
882 	* returns a string of the file's content.
883 	*/
884 	string getFileContent(string name) {
885 		string ret = "";
886 		int i = 0;
887 
888 		for (i = 0; i != cgiVals.length; ++i) {
889 			if (cgiVals[i] && 
890 				cgiVals[i].name == name && 
891 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
892 				ret = cgiVals[i].fData.content.idup;
893 				break;
894 			}
895 		}
896 
897 		return ret;
898 	}
899 
900 	/**
901 	* Get the content of a file in the cgi environment with string name and the index.
902 	* keep in mind that we can have the same cgi variable with string name several times
903 	* index allows us to specify which one we want. 
904 	*
905 	* returns a string of the file's content.
906 	*/
907 	string getFileContent(string name, int index) {
908 		string ret = "";
909 		int i = 0;
910 		int count = 0;
911 
912 		for (i = 0; i != cgiVals.length; ++i) {
913 			if (cgiVals[i] && 
914 				cgiVals[i].name == name && 
915 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
916 				if (count == index) {
917 					ret = cgiVals[i].fData.content.idup;
918 					break;
919 				}
920 				count++;
921 			}
922 		}
923 
924 		return ret;
925 	}
926 
927 	/**
928 	* Get a file from the cgi enviroment of string name
929   	* returns a CGIFILE class.	
930 	*/
931 	CGIFILE getFile(string name) {
932 		CGIFILE ret;
933 		int i = 0;
934 
935 		for (i = 0; i != cgiVals.length; ++i) {
936 			if (cgiVals[i] && 
937 				cgiVals[i].name == name && 
938 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
939 				ret = cgiVals[i].fData;
940 				break;
941 			}
942 		}
943 
944 		return ret;
945 	}
946 
947 
948 	/**
949 	*
950 	*/
951 	CGIFILE getFile(string name, int index) {
952 		CGIFILE ret;
953 		int i = 0;
954 		int count = 0;
955 
956 		for (i = 0; i != cgiVals.length; ++i) {
957 			if (cgiVals[i] && 
958 				cgiVals[i].name == name && 
959 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
960 				if (count == index) {
961 					ret = cgiVals[i].fData;
962 					break;
963 				}
964 				count++;
965 			}
966 		}
967 
968 		return ret;
969 	}
970 
971 	/**
972 	* Dumps the system environment variables to the page.
973 	*/
974 	void dumpEnv() {
975 		auto env = environment.toAA();
976 		auto keys = env.keys;
977 		auto values = env.values;
978 		 
979     	for (int i = 0; i != values.length; ++i) {
980         	writefln("%s = %s<br>",keys[i],values[i]);
981     	}
982 
983 	}
984 
985 	/**
986 	* Dumps the contents of the CGI class to the page. Very useful for debuging
987 	*/
988 	void dump() {
989 		int i = 0;
990 		writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=<br>");
991 		writeln("+ Environment                                              =<br>");
992 		writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=<br>");
993 		writefln("REQUEST_METHOD = %s<br>",this.REQUEST_METHOD);
994 		writefln("HTTP_USER_AGENT = %s<br>",this.HTTP_USER_AGENT);
995 		writefln("PATH_INFO = %s<br>",this.PATH_INFO);
996 		writefln("CONTENT_TYPE = %s<br>",this.CONTENT_TYPE);
997 		writefln("CONTENT_LENGTH = %s<br>",this.CONTENT_LENGTH);
998 		writefln("QUERY_STRING = %s<br>",this.QUERY_STRING);
999 		writefln("HTTP_COOKIE = %s<br>",this.HTTP_COOKIE);
1000 		writefln("BASE_URL = %s<br>",this.BASE_URL);
1001 		writefln("SCRIPT_NAME = %s<br>",this.SCRIPT_NAME);
1002 		writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=<br>");
1003 		writeln("+ CGI vals                                                 =<br>");
1004 		writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=<br>");
1005 		for (i = 0; i != cgiVals.length; ++i) {
1006 			if (cgiVals[i]) {
1007 				if (cgiVals[i].type == CGI_VAR_TYPE.FILE) {
1008 					writefln("Name = (%s) | filename = (%s) |" ~
1009 							" content-type (%s)| forward = (%d) <br>",
1010 							cgiVals[i].name,cgiVals[i].fData.filename,
1011 							cgiVals[i].fData.content_type,cgiVals[i].mode);
1012 				} else {
1013 					writefln("Name = (%s) | value = (%s) | forward = (%d)<br>",
1014 						cgiVals[i].name,cgiVals[i].value,cgiVals[i].mode);
1015 				}
1016 			}	
1017 		}
1018 		writeln("+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=<br>");
1019 
1020 	}
1021 
1022 	/**
1023 	* Print to standard out http headers initialize the page.
1024 	* 
1025 	* handles the standard http header of content type a carriage 
1026 	* return followed by two new lines, etc... for you. so that the
1027 	* web server can return correct data back to the calling browser.
1028 	* 
1029 	* depending on the MIMETYPE value we have different behaviors
1030 	*
1031 	* Type TEXT_HTML, will write out the http header, cookies, and start 
1032 	*  the html for you if you want to be lazy and just concentrate whats 
1033 	*  the <body> tag only.  
1034 	* 
1035 	* Type TEXT_HTML_NO_CACHE is basically the same behavior as TEXT_HTML,
1036 	*  but it sets the browsers cache behavior to not cache the page.
1037 	* 
1038 	* Types TEXT_HTML_RAW and TEXT_HTML_NO_CACHE_RAW write out the HTTP header
1039 	*  information and cookie information only.  It does not start the HTML for you!
1040 	*  you will have to write to standard out valid html.
1041 	*
1042     * type RAW_TYPE is for when you have a content type that is not text/html or 
1043     *  text/plain.  for example you want to write to standard out JSON or XML data.
1044     *  you will have to set valid mime type for what you are sending back to the browser. 
1045 	*
1046 	* types TEXT_PLAIN and TEXT_PLAIN_NO_CACHE is a lazy short cut if you just want to return
1047 	*  to the browser anything that has the mime content-type of text/plain.   
1048 	*/
1049 	void pageStart() {
1050 		if (mime_type_set) {
1051 			this.pageStart(mime_type);
1052 		} else {
1053 			this.pageStart(MIMETYPE.TEXT_PLAIN);
1054 		}
1055 	}
1056 
1057 	void pageStart(MIMETYPE type) {
1058 		string content_type = "";
1059 		
1060 		switch (type) {
1061 			case MIMETYPE.TEXT_HTML:			
1062 			case MIMETYPE.TEXT_HTML_NO_CACHE:
1063 			case MIMETYPE.TEXT_HTML_RAW:
1064 			case MIMETYPE.TEXT_HTML_NO_CACHE_RAW:
1065 				content_type = "text/html";
1066 				break;
1067 			case MIMETYPE.RAW_TYPE:
1068 				content_type = custom_content_type;
1069 				break;
1070 			case MIMETYPE.TEXT_PLAIN:
1071 			case MIMETYPE.TEXT_PLAIN_NO_CACHE:
1072 			//Fall through to default
1073 			default:
1074 				content_type="text/plain";
1075 				break;
1076 		}
1077 
1078 
1079 		if( type == MIMETYPE.TEXT_HTML_NO_CACHE ||
1080 			type == MIMETYPE.TEXT_PLAIN_NO_CACHE ||
1081 			type == MIMETYPE.TEXT_HTML_NO_CACHE_RAW) {
1082 			write("Expires: 0\r\n");
1083 			write("Cache-Control: no-store\r\n");
1084 		}
1085 
1086 
1087 		if (content_type == "text/html") {					
1088 			if (cookie.data.length != 0) {
1089 				writef("Set-Cookie: %s\r\n",cookie.toString());
1090 			}
1091 		}
1092 
1093 		if (content_type.length != 0 ) {
1094 			writef("Content-type: %s\r\n",content_type);
1095 			write("\n");
1096 			if (type == MIMETYPE.TEXT_HTML_NO_CACHE || type == MIMETYPE.TEXT_HTML) {
1097 				write("<html>");
1098 				write("<head>");
1099 				if (type == MIMETYPE.TEXT_HTML_NO_CACHE) {
1100 					write("<meta http-equiv=\"CACHE-CONTROL\" " ~
1101 							"content=\"NO-CACHE\">\r\n");
1102 				}
1103 			}
1104 		} 
1105 	}
1106 
1107 	/**
1108 	 * stream the contents of a file back to the browser.  you 
1109 	 * will have to store the file in the cgi class instance by 
1110 	 * setting a CGIVAL with CGIFILE class.  
1111 	 */
1112 	void streamFile(string name) {
1113 		this.streamFile(name,0);
1114 	}
1115 
1116 	void streamFile(string name, int index) {
1117 		int i = 0;
1118 		int count = 0;
1119 
1120 		for (i = 0; i != cgiVals.length; ++i) {
1121 			if (cgiVals[i] &&
1122 				cgiVals[i].name == name &&
1123 				cgiVals[i].type == CGI_VAR_TYPE.FILE) {
1124 				if (count == index) {
1125 					writef("Content-type: %s\r\n\n",cgiVals[i].fData.content_type);
1126 					writef("%s",cgiVals[i].fData.content);
1127 					break;
1128 				}
1129 				count++;
1130 			}
1131 		}
1132 	}
1133 
1134 	/**
1135 	* end the page being returned
1136 	*
1137 	* if you used one of the shortcut MIMETYPE's TEXT_HTML 
1138 	* and TEXT_HTML_NO_CACHE this will close the <html> tag 
1139 	* created for you. 
1140 	*/
1141 	void pageEnd() {
1142 		if (mime_type_set) {
1143 			this.pageEnd(mime_type);
1144 		} else {
1145 			this.pageEnd(MIMETYPE.TEXT_HTML);
1146 		}
1147 	}
1148 	
1149 	/**
1150 	*
1151 	*/
1152 	void pageEnd(MIMETYPE type) {
1153 		switch (type) {
1154 			case MIMETYPE.TEXT_HTML:
1155 			case MIMETYPE.TEXT_HTML_NO_CACHE:
1156 				write("</html>\n");
1157 				break;
1158 			default:
1159 				break;
1160 		}
1161 	}
1162 
1163 	/**
1164 	 * set the MIMETYPE.
1165 	 */  
1166 	void setMimeType(MIMETYPE type) {
1167 		mime_type = type;
1168 		mime_type_set=1;
1169 	}
1170 
1171 	void setMimeType(string custom_content_type) {
1172 		mime_type=MIMETYPE.RAW_TYPE;
1173 		mime_type_set=1;
1174 		this.custom_content_type = custom_content_type;
1175 	}
1176 
1177 	/**
1178 	 * get the current MIMETYPE
1179 	 */
1180 	MIMETYPE getMimeType() {
1181 		return mime_type;
1182 	}
1183 
1184 	/**
1185 	 * set the class instance mime type to common mimetypes 
1186 	 * based on a file extension.
1187 	 *
1188 	 * Note: Does not include every possible mime type, and 
1189 	 * 	will be added to in future releases. It could also 
1190 	 *  be smarter by doing a look up, but I'm trying to be 
1191 	 *  light weight and not require a support file, or 
1192 	 *  having to make a external connection to get data. if
1193 	 *  you need that kind of functionality, then it's up to 
1194 	 *  the application developer to make that design call.
1195 	 *  
1196 	 */
1197 	void discoverMimeType(string extension) {
1198 		// figure out our content type based off the extension. 
1199 		switch (extension) {
1200 			case "widget":
1201 			case "WIDGET":
1202 			case "html":
1203 			case "HTML":
1204 				this.setMimeType(MIMETYPE.TEXT_HTML_RAW);
1205 				break;
1206 			case "txt":
1207 			case "TEXT":
1208 			case "text/plain":
1209 				this.setMimeType(MIMETYPE.TEXT_PLAIN);
1210 				break;
1211 			case "xml":
1212 			case "XML":
1213 				this.setMimeType("application/xml");
1214 				break;
1215 			case "js":
1216 			case "JS":
1217 				this.setMimeType("application/javascript");
1218 				break;
1219 			case "css":
1220 			case "CSS":
1221 				this.setMimeType("text/css");
1222 				break;
1223 			case "png":
1224 			case "PNG":
1225 				this.setMimeType("image/png");
1226 				break;
1227 			case "jpg":
1228 			case "JPG":
1229 			case "jpeg":
1230 			case "JPEG":
1231 				this.setMimeType("image/png");
1232 				break;
1233 			case "svg":
1234 			case "SVG":
1235 				this.setMimeType("image/svg+xml");
1236 				break;
1237 			case "gif":
1238 			case "GIF":
1239 				this.setMimeType("image/gif");
1240 				break;
1241 			case "tif":
1242 			case "TIF":
1243 			case "tiff":
1244 			case "TIFF":
1245 				this.setMimeType("image/tiff");
1246 				break;
1247 			default:
1248 				this.setMimeType(MIMETYPE.TEXT_PLAIN);
1249 				break;
1250 		}
1251 	}
1252 
1253 	/**
1254 	* set the cookie.
1255 	*/
1256 	void setCookie(COOKIE cookie) {
1257 		this.cookie = cookie;
1258 	}
1259 
1260 	/**
1261 	* Get the cookie.
1262 	*/
1263 	COOKIE getCookie() {
1264 		return this.cookie;
1265 	}
1266 	
1267 	/**
1268 	* lazy javascript that you can use to tell the browser 
1269 	* to keep scrolling down as you write content. This is 
1270 	* handy if you said don't cache, and your web server 
1271 	* is set up to not compress the returned data to browser.
1272 	*
1273 	* for example you want to write a UI that you upload a .csv
1274 	* file to, and you have to do things with the data, and you 
1275 	* have implemented a loging system that writes to a file and
1276 	* stdout at the same time,  you could display your logs real
1277 	* time back to a browser as you process the .csv file.  
1278 	*
1279 	* it's just been useful.  use it or don't.
1280 	*/
1281 	void autoScrollInit() {
1282 		write("<script type=\"text/javascript\">\n");
1283 		write("function AutoScroll()\n");
1284 		write("{\n");
1285 		write("   var cw = document.body.clientHeight;\n");
1286 		write("   var fh = document.body.scrollHeight;\n");
1287 		write("   if (fh > cw) {\n");
1288 		write("      document.body.scrollTop = fh;\n");
1289 		write("   }\n");
1290 		write("}\n");
1291 		write("</script>\n");
1292 	}
1293 
1294 	/**
1295 	* tell the browser to run the AutoScroll javascript. 
1296 	*/
1297 	void runAutoScroll() {
1298 		write("<script type=\"text/javascript\">\n");
1299 		write("AutoScroll();");
1300 		write("</script>\n");
1301 	}
1302 
1303 	/**
1304 	* getter method for error_msg.   
1305 	*/
1306 	string getErrorMsg() {
1307 		return error_msg;
1308 	}
1309 }